package com.example.zyl.common.query.aspect;

import com.example.zyl.common.exception.ServiceException;
import com.example.zyl.common.query.annotation.ApiLimit;
import com.example.zyl.common.utils.ConditionUtils;
import com.example.zyl.common.utils.constant.RedisConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;


/**
 * 限流aop
 */
@Component
@Aspect
@Slf4j
@RequiredArgsConstructor
public class LimitAspect {

    private final RedisTemplate<String, Object> redisTemplate;

    private final DefaultRedisScript<Long> script;

    @Before(value = "@annotation(com.example.zyl.common.query.annotation.ApiLimit)")
    public void before(JoinPoint point) {
        //获取参数
        Object[] args = point.getArgs();

        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取注解
        ApiLimit apiLimit = signature.getMethod().getAnnotation(ApiLimit.class);
        //获取限流次数
        int count = apiLimit.count();
        //限流事件
        int time = apiLimit.time();
        //限流策略
        String key = apiLimit.rule().execute(args);
        //拼接key
        String redisKey = RedisConstant.REDIS_KEY_PREFIX.concat(key);
        try {
            //查询redis，配合lua
            Long number = redisTemplate.execute(script, Collections.singletonList(redisKey), count, time);
            ConditionUtils.throwException((number == null || number.intValue() > count)).accept("访问过于频繁，请稍候再试");
            log.info("限制请求 ==> [{}],当前请求 ==> [{}], 当前缓存 ==> [{}]", count, time, key);
        } catch (Exception e) {
            throw new ServiceException("服务器限流异常，请稍后重试");
        }
    }

}
